/*
XBaseTools
Copyright (C) 2008 Matthew Perry

This library/program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

This libary/program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

using System;
using System.Text;

namespace BaseTools.Strings
{
	/// <summary>
	/// Parses character streams with a ParseInfo
	/// </summary>
	public class StringParser
	{
		ParseInfo pi;
		
		/// <summary>
		/// Create a new StringParser
		/// </summary>
		/// <param name="pi">The ParseInfo to use</param>
		public StringParser(ParseInfo pi)
		{
			this.pi = pi;
		}
		
		/// <summary>
		/// Encode a string (convert to escaped form)
		/// </summary>
		/// <param name="str"></param>
		/// <returns></returns>
		public string EncodeString(string str)
		{
			return EncodeStream(new StringCharStream(str));
		}
		
		/// <summary>
		/// Encode a string (convert to escaped form)
		/// </summary>
		/// <param name="str"></param>
		/// <param name="sb"></param>
		/// <returns></returns>
		public void EncodeString(string str, StringBuilder sb)
		{
			EncodeStream(new StringCharStream(str), sb);
		}
		
		/// <summary>
		/// Encode a stream (convert to escaped form)
		/// </summary>
		/// <param name="stream"></param>
		/// <returns></returns>
		public string EncodeStream(ICharStream stream)
		{
			string output = "";
			while(stream.Available())
			{
				char ch = stream.Next();
				
				int epos = EscapeSequence.FindByOutput(pi.Escapes, ch);
				
				if (epos == -1)
					output += ch.ToString();
				else
					output += pi.EscapeChar.ToString() + pi.Escapes[epos].Sequence.ToString();
			}
			return output;
		}
		
		/// <summary>
		/// Encode a stream and put it into a StringBuilder
		/// </summary>
		/// <param name="stream"></param>
		/// <param name="output"></param>
		public void EncodeStream(ICharStream stream, StringBuilder output)
		{
			while(stream.Available())
			{
				char ch = stream.Next();
				
				int epos = EscapeSequence.FindByOutput(pi.Escapes, ch);
				
				if (epos == -1)
					output.Append(ch);
				else
					output.Append(pi.EscapeChar).Append(pi.Escapes[epos].Sequence);
			}
		}
		
		/// <summary>
		/// Reads a block, stopping at blocks, end of literals and stop chars
		/// </summary>
		/// <param name="stream">The stream</param>
		/// <returns>A block of data</returns>
		public StringBlock ReadBlock(ICharStream stream)
		{
			string data = "";
			SkipWhitespace(stream);
			bool literal = false;
			bool escape = false;
			bool ft = false;
			
			while(stream.Available())
			{
				char ch = stream.Next();
				
				if (escape)
				{
					int epos = EscapeSequence.FindBySequence(pi.Escapes, ch);
					
					if (epos == -1)
						data += ch.ToString();
					else
						data += pi.Escapes[epos].Output;
					
					escape = false;
				}
				else
				{
					if (ch == pi.EscapeChar && literal)
						escape = true;
					else if (ch == pi.LiteralChar && literal)
						return new StringBlock(data, true);
					else if (ch == pi.LiteralChar && !literal)
						literal = true;
					else if (Array.IndexOf(pi.Whitespace, ch) != -1 && !literal)
						return new StringBlock(data, false);
					else if (!literal && Array.IndexOf(pi.SpecialChars, ch) != -1)
					{
						if (ft)
							stream.Unread();
						else
							data += ch.ToString();
							
						return new StringBlock(data, false);
					}
					else
						data += ch.ToString();
				}
				
				ft = true;
			}
			
			return new StringBlock(data, literal);
		}
		
		/// <summary>
		/// Skips a line, determined by the line chars
		/// </summary>
		/// <param name="stream">The stream</param>
		public void SkipLine(ICharStream stream)
		{
			while(stream.Available())
			{
				char ch = stream.Next();
				
				if (Array.IndexOf(pi.Lines, ch) != -1)
					break;
			}
		}
		
		/// <summary>
		/// Skip the whitespace in a stream
		/// </summary>
		/// <param name="stream">The stream</param>
		public void SkipWhitespace(ICharStream stream)
		{
			char c;
			while(stream.Available())
			{
				c = stream.Next();
				
				if (Array.IndexOf(pi.Whitespace, c) == -1)
				{
					//return c.ToString();
					stream.Unread();
					return;
				}
			}
		}
		
		/// <summary>
		/// Skip until a specific string is reached
		/// </summary>
		/// <param name="stream">The stream</param>
		/// <param name="until">String to find</param>
		public void SkipUntil(ICharStream stream, string until)
		{
			if (until == null || until.Length == 0)
				return;
			char c;
			int ca = 0;
			while(stream.Available())
			{
				c = stream.Next();
				
				if (c == until[ca])
				{
					ca++;
					if (ca == until.Length)
						break;
				}
				else
				{
					ca = 0;
				}
			}
		}
	}
}
